package com.jeeplus.modules.web.socket;

import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.jeeplus.common.json.AjaxJson;
import com.jeeplus.common.utils.concurrent.ThreadUtil;
import com.jeeplus.modules.web.entity.GetTotalVO;
import com.jeeplus.modules.web.mapper.YJRoomUserMapper;
import com.jeeplus.modules.web.service.YjScoreRecordService;
import org.javatuples.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 十戒丶
 * @date 2021-02-03 20:37:40
 */
@ServerEndpoint(value = "/web_socket/user_msg/{openid}/{room_flag}")
public class UserMsgSocket {
    private static final Logger log = LoggerFactory.getLogger(UserMsgSocket.class);

    private static final AtomicInteger onlineCount = new AtomicInteger(0);
    //openid，房间唯一号，<房间号， Socket对象>
    public volatile static Table<String, String, Pair<String, UserMsgSocket>> webSocketTable = HashBasedTable.create();
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    public volatile Session session;
    private String openid;
    private String roomFlag;

    @OnOpen
    public void onOpen(@PathParam("openid") String openid, @PathParam("room_flag") String roomFlag, Session session) throws IOException {
        log.info("[{}][{}]正在打开连接", openid, roomFlag);
        this.session = session;
        if (StrUtil.isBlank(openid) || StrUtil.isBlank(roomFlag)) {
            errorDelayClose(Dict.create().set("msg", "参数异常"));
            return;
        }
        //查询是否已经存在webSocketTable
        clearCache(openid, roomFlag);
        //查询账号是否存在，并且存在这个房间中
        final YJRoomUserMapper yjRoomUserMapper = SpringUtil.getBean(YJRoomUserMapper.class);
        //判断是否在房间并获取房间号
        final String room = yjRoomUserMapper.findIsExisRoom(openid, roomFlag);
        if (StrUtil.isBlank(room)) {
            errorDelayClose(Dict.create().set("msg", "您不在房间中"));
            return;
        }
        webSocketTable.put(openid, roomFlag, Pair.with(room, this));
        this.openid = openid;
        this.roomFlag = roomFlag;
        this.session.getBasicRemote().sendText(new ParamJson(MsgCode.code_ok.getValue0(), Dict.create().set("msg", "连接成功")).toJson());
        log.info("[{}][{}]连接成功,当前连接数：{}", openid, roomFlag, onlineCount.get());
        onlineCount.addAndGet(1);
    }

    @OnClose
    public void onClose() throws IOException {
        log.info("[{}][{}]关闭连接", openid, roomFlag);
        if (webSocketTable.get(openid, roomFlag) != null) {
            onlineCount.addAndGet(-1);
        }
        //查询是否已经存在webSocketTable
        clearCache(openid, roomFlag);
    }

    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        if (StrUtil.isBlank(openid) || StrUtil.isBlank(roomFlag)) {
            errorDelayClose(Dict.create().set("msg", "连接失败"));
            return;
        }
        if (message == null || !JSONUtil.isJson(message)) {
            this.session.getBasicRemote().sendText(new ParamJson(MsgCode.code_refuse.getValue0(), Dict.create().set("msg", "数据异常")).toJson());
            return;
        }
        try {
            final ParamJson paramJson = JSONUtil.toBean(message, ParamJson.class);
            if (paramJson.type == null) {
                this.session.getBasicRemote().sendText(new ParamJson(MsgCode.code_refuse.getValue0(), Dict.create().set("msg", "数据接口类型异常")).toJson());
                return;
            }
            if (paramJson.type.equals(MsgCode.type_test.getValue0())) {
                final YjScoreRecordService yjScoreRecordService = SpringUtil.getBean(YjScoreRecordService.class);
                //调用测试的方法
                final AjaxJson test = yjScoreRecordService.test(openid, roomFlag, 1, 10);
                this.session.getBasicRemote().sendText(new ParamJson(MsgCode.type_test.getValue0(), test).toJson());
            } else if (paramJson.type.equals((MsgCode.type_test2.getValue0()))) {
                final YjScoreRecordService yjScoreRecordService = SpringUtil.getBean(YjScoreRecordService.class);
                final AjaxJson test = yjScoreRecordService.test(openid, roomFlag, 2, 10);
                this.session.getBasicRemote().sendText(new ParamJson(MsgCode.type_test.getValue0(), test).toJson());
            } else if (paramJson.type.equals((MsgCode.push_DATA.getValue0()))) {
                this.sendMessage();
            } else if (paramJson.type.equals(MsgCode.type_heart.getValue0())) {
                log.info("[{}][{}]执行心跳", openid, roomFlag);
                this.session.getBasicRemote().sendText(new ParamJson(MsgCode.type_heart.getValue0(), null).toJson());
            }
        } catch (Exception exception) {
            log.error("接收消息异常:{}", message, exception);
        }
    }

    public void sendMessage() throws IOException {
        System.out.println("进入sendMessage");
        YjScoreRecordService yjScoreRecordService = SpringUtil.getBean(YjScoreRecordService.class);
        System.out.println("sendMessage的rooflag===" + roomFlag);
        final Map<String, Pair<String, UserMsgSocket>> row = UserMsgSocket.webSocketTable.column(roomFlag);

        for (String key : row.keySet()) {
            GetTotalVO getTotalVO = yjScoreRecordService.getTotal(key, roomFlag, row.get(key).getValue0());
            getTotalVO.setType("4");
            System.out.println("sendMessage的openId==" + key);
            // JSONObject json = JSONObject.fromObject(getTotalVO);
            // System.out.println("jsonObj=="+JSONArray.toJSON(getTotalVO));
            // System.out.println("jsonString=="+JSONArray.toJSON(getTotalVO).toString());
            //row.get(key).getValue1().session.getBasicRemote().sendText(new ParamJson(MsgCode.push_DATA.getValue0(), json).toJson());
            row.get(key).getValue1().session.getBasicRemote().sendText(JSONArray.toJSON(getTotalVO).toString());
        }
//
//        for (Pair<String, UserMsgSocket> value : row.values()) {
//            String roomNum = UserMsgSocket.webSocketTable.get(value.getValue1().openid, value.getValue1().roomFlag).getValue0();
//            HashMap<String, Object> map = yjScoreRecordService.getTotal(openid, roomFlag, roomNum);
//            // System.out.println("return json=="+new ParamJson(MsgCode.push_DATA.getValue0(), JSONObject.fromObject(data)).toJson());
//         /*   JSONArray jsonArray = new JSONArray();
//            jsonArray.add(data);*/
//            //JSONObject jsonObj=new JSONObject(data);
//            //System.out.println("return json=="+ JSON.toJSONString(map));
//            // this.session.getBasicRemote().sendText(JSON.toJSONString(map));
//            map.put("type", MsgCode.push_DATA.getValue0());
//            this.session.getBasicRemote().sendText(JSON.toJSONString(map));
//
//        }

    }

    public void clearCache(String openid, String roomFlag) throws IOException {
        if (webSocketTable.get(openid, roomFlag) != null) {
            final Pair<String, UserMsgSocket> pair = webSocketTable.get(openid, roomFlag);
            //存在关闭之前连接
            if (pair.getValue1().session.isOpen()) {
                log.info("[{}][{}]关闭连接", openid, roomFlag);
                pair.getValue1().session.getBasicRemote().sendText(new ParamJson(MsgCode.code_refuse.getValue0(), Dict.create().set("msg", "账号在别处登录")).toJson());
            }
            webSocketTable.remove(openid, roomFlag);
        }
    }

    public void errorDelayClose(Object data) throws IOException {
        log.info("[{}]连接参数异常[{}]", roomFlag, data);
        this.session.getBasicRemote().sendText(new ParamJson(MsgCode.code_refuse.getValue0(), data).toJson());
        ThreadUtil.sleep(2 * 1000);
        this.session.close();
    }

    public static class ParamJson {
        private Object data;
        private String uuid;
        private String msg;
        private Integer type;

        public ParamJson(Pair<Integer, String> pair, Object data) {
            this.data = data;
            this.msg = pair.getValue1();
            this.type = pair.getValue0();
        }

        public ParamJson(Integer type, Object data) {
            this.data = data;
            this.type = type;
        }

        public ParamJson(String uuid, Integer type, Object data) {
            this.data = data;
            this.type = type;
            this.uuid = uuid;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        public Integer getType() {
            return type;
        }

        public void setType(Integer type) {
            this.type = type;
        }

        public Object getData() {
            return data;
        }

        public void setData(Object data) {
            this.data = data;
        }

        public String getUuid() {
            return uuid;
        }

        public void setUuid(String uuid) {
            this.uuid = uuid;
        }


        public boolean verifyFormat(String msg) {
            return JSONUtil.isJson(msg);
        }

        public ParamJson toBean(String msg) {
            return JSONUtil.toBean(msg, ParamJson.class);
        }

        public String toJson() {
            return JSONUtil.toJsonStr(this);
        }
    }

    public static class MsgCode {
        public static Pair<Integer, String> code_ok = Pair.with(-1, "成功");
        public static Pair<Integer, String> code_refuse = Pair.with(500, "拒绝|强制退出|异常");

        public static Pair<Integer, String> type_test = Pair.with(1, "测试");
        public static Pair<Integer, String> type_test2 = Pair.with(2, "测试");
        public static Pair<Integer, String> type_push = Pair.with(3, "测试推送");
        public static Pair<Integer, String> push_DATA = Pair.with(4, "分数统计数据");
        public static Pair<Integer, String> type_help_record = Pair.with(5, "帮助记录");
        public static Pair<Integer, String> type_next_game = Pair.with(6, "下一局");

        public static Pair<Integer, String> type_heart = Pair.with(7, "心跳");
    }
}
